package us.versus.them.weeds.demos;

import flash.display.GradientType;
import flash.display.Graphics;
import flash.display.InterpolationMethod;
import flash.display.MovieClip;
import flash.display.SpreadMethod;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Matrix;
import flash.geom.Point;

import us.versus.them.weeds.Vertlet;
import us.versus.them.weeds.constraints.Angle;
import us.versus.them.weeds.constraints.Box;
import us.versus.them.weeds.constraints.Constraint;
import us.versus.them.weeds.constraints.Segment;

class Angularia {

	var clip:MovieClip;
	
	var mouse:Vertlet;

	var satellites:Array< Vertlet >;
	var constraints:Array< Constraint >;

	var box:Box;

	var level:Int;
	var draw_the_polgyon : Bool;

	var dx:Float;
	var dy:Float;

	var mouseIsDown:Bool;

	public static function main() {
		new Angularia();
	}

	public function new( ?clip:MovieClip ) {
		if ( null == clip ) clip = flash.Lib.current;
		this.clip = clip;
		
		this.reset();
		this.clip.addEventListener( Event.ENTER_FRAME, enter_frame );
		this.clip.stage.addEventListener( MouseEvent.MOUSE_DOWN, mouse_down );
		this.clip.stage.addEventListener( MouseEvent.MOUSE_UP, mouse_up );
	}

	public function mouse_down( _ ) {
		reset();
		this.mouseIsDown = true;
	}

	public function mouse_up( _ ) {
		this.mouseIsDown = false;
	}

	public function reset() {
		this.level = 1;
		this.mouseIsDown = false;

		this.draw_the_polgyon = !this.draw_the_polgyon;

		this.constraints = new Array< Constraint >();
		this.satellites = new Array< Vertlet >();

		this.setup();
	}

	public function setup() {
		var w2 = this.clip.mouseX;
		var h2 = this.clip.mouseY;

		var n = 120;
		var r = 120;
		var ainc = 44.0 / 7.0 / n;
		var angle = 0.0;
		var prv:Vertlet = null;
		var first:Vertlet = null;

		this.dx = this.dy = 10;

		var rigid_angles   = true;
		var m2s_rigidity   = 1.9;
		var s2s_rigidity   = 0.2;
		var mouse_mass     = 90.0;
		var satellite_mass = 1.0;

		this.mouse = new Vertlet( w2, h2, mouse_mass );

		for ( i in 0 ... n ) {
			var pt = new Vertlet( 
				  mouse.current.x + Math.cos( angle ) * r
				, mouse.current.y + Math.sin( angle ) * r
				, satellite_mass
			);

			satellites.push( pt );
			constraints.push( new Angle( mouse, pt, angle, rigid_angles ) );
			constraints.push( new Segment( mouse, pt, m2s_rigidity ) );

			if ( null != prv ) {
				constraints.push( new Segment( prv, pt, s2s_rigidity ) );
			}
			prv = pt;
			if ( null == first ) {
				first = pt;
			}

			angle += ainc;
		}
		if ( null != first && null != prv ) {
			constraints.push( new Segment( prv, first, s2s_rigidity ) );
		}

		this.box = new Box( this.satellites, clip.stage.stageWidth, clip.stage.stageHeight );
		constraints.push( this.box );
	}

	public function enter_frame( _ ) {
		move( this.clip.mouseX, this.clip.mouseY );
		draw( clip.graphics );
	}

	public function move( x, y ) {
		enforceConstraints();

		if ( this.mouseIsDown ) {
			mouse.current.x = x;
			mouse.current.y = y;
		} else {
			mouse.current.x += dx;
			mouse.current.y += dy;
			if ( 0 != this.box.bounds & Box.X_TOO_SMALL ) {
				if ( dx < 0 ) dx *= -1;
			}
			if ( 0 != this.box.bounds & Box.X_TOO_BIG ) {
				if ( dx > 0 ) dx *= -1;
			}
			if ( 0 != this.box.bounds & Box.Y_TOO_SMALL ) {
				if ( dy < 0 ) dy *= -1;
			}
			if ( 0 != this.box.bounds & Box.Y_TOO_BIG ) {
				if ( dy > 0 ) dy *= -1;
			}
		}
	}


	public function enforceConstraints() {
		// gravity
		for ( s in satellites ) {
			s.current.y += 0;
		}

		for ( i in 0 ... level ) {
			for ( c in constraints ) {
				c.enforce();
			}
		}
	}

	public function draw( g:Graphics ) { 
		g.clear();

		if ( draw_the_polgyon ) {
			drawPoly( g );
		} else {
			this.drawDot( g, mouse );
			for ( pt in satellites ) drawDot( g,pt );
			drawConstraints( g );
		}
	}

	public function drawPoly( g:Graphics ) {
		//g.beginFill( 0xFFAAAA );
		
		var min_x = 100000.0;
		var min_y = 100000.0;
		var max_x = -10000.0;
		var max_y = -10000.0;
		for ( pt in satellites ) {
			if ( pt.current.x < min_x ) min_x = pt.current.x;
			if ( pt.current.y < min_y ) min_y = pt.current.y;
			if ( pt.current.x > max_x ) max_x = pt.current.x;
			if ( pt.current.y > max_y ) max_y = pt.current.y;
		}
		var gx = Math.floor( min_x + 0.9 * ( max_x - min_x ) );
		var gy = Math.floor( min_y + 0.9 * ( max_y - min_y ) );

		var gw = 0.8 * ( max_x - min_x );
		var gh = 0.8 * ( max_y - min_y );


		var matrix:Matrix = new Matrix();
		matrix.createGradientBox( gw, gh, Math.PI / 2, min_x, min_y ) ;

		//http://lionpath.com/haxeflashtutorial/release/chap01.html
		g.beginGradientFill(
			GradientType.RADIAL
			, [0xFFCCCC, 0xCC6666 ] // colors
			, [1, 1]                // alphas
			, [0, 255]              // ratios
			, matrix
			, SpreadMethod.PAD
			, InterpolationMethod.LINEAR_RGB
			, 0
		);

		var x = g.moveTo;
		for ( pt in satellites ) {
			x( pt.current.x, pt.current.y );
			x = g.lineTo;
		}
		g.endFill();
	}

	public function drawDot( g:Graphics , v:Vertlet ) {
		g.lineStyle( 0.4, 0 );
		g.beginFill( 0xAAAAFF );
		g.drawCircle( v.current.x, v.current.y, 4 );
		g.endFill();
	}

	public function drawConstraints( g:Graphics ) {
		for ( c in constraints ) {
			if ( Std.is( c, Segment ) ) {
				var s = cast( c, Segment );
				g.lineStyle( 2, 0 );
				g.beginFill( 0xAAAAFF );
				g.moveTo( s.start.current.x, s.start.current.y );
				g.lineTo( s.stop.current.x, s.stop.current.y );
				g.endFill();
			}
		}
	}
		
}
